package com.ebupt.migu.music.autoTri.Service.Impl;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.ebupt.migu.music.autoTri.Entity.message.Body;
import com.ebupt.migu.music.autoTri.Entity.message.MessageInfo;
import com.ebupt.migu.music.autoTri.Entity.message.UserSender;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.HtmlUtils;

import com.alibaba.fastjson.JSONObject;
import com.ebupt.migu.music.api.entity.PushContentObject;
import com.ebupt.migu.music.api.entity.PushPolicyObject;
import com.ebupt.migu.music.api.entity.PushRequestBody;
import com.ebupt.migu.music.autoTri.Mapper.AutoTriMapper;
import com.ebupt.migu.music.autoTri.Service.AutoTriService;
import com.ebupt.migu.music.autoTri.Service.PushListService;
import com.ebupt.migu.music.common.api.ApiResponse;
import com.ebupt.migu.music.common.constants.StrategyConstants;
import com.ebupt.migu.music.common.entity.Page;
import com.ebupt.migu.music.common.entity.ResultObject;
import com.ebupt.migu.music.common.exception.MyException;
import com.ebupt.migu.music.common.exception.StatusEnum;
import com.ebupt.migu.music.common.sms.Sender;
import com.ebupt.migu.music.common.util.DateUtil;
import com.ebupt.migu.music.common.util.HttpUtils;
import com.ebupt.migu.music.common.util.JsonUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * Created by Intellij IDEA. User: liuyangyang@cd.ebupt.com Date: 2020/6/9
 */
@Slf4j
@Service
public class AutoTriServiceImpl implements AutoTriService {

    @Value("${push.ip}")
    private String PUSH_IP;
    @Value("${push.app_id}")
    private String PUSH_APP_ID;
    @Value("${push.app_key}")
    private String PUSH_APP_KEY;
    @Value("${push.app_secret}")
    private String PUSH_APP_SECRET;
    @Value("${push.push_url}")
    private String PUSH_URL;
    @Value("${path.image_server_path}")
    private String IMAGE_SERVER_PATH;
    @Value("${popup.ip}")
    private String POPUP_IP;
    @Value("${popup.port}")
    private int POPUP_PORT;
    @Value("${popup.user_name}")
    private String POPUP_USER_NAME;
    @Value("${popup.password}")
    private String POPUP_PASSWORD;
    @Value("${popup.local_path}")
    private String POPUP_LOCAL_PATH;
    @Value("${popup.remote_path}")
    private String POPUP_REMOTE_PATH;
    @Value("${msg.appid}")
    private String MSG_APP_ID;
    @Value("${msg.account}")
    private String MSG_ACCOUNT;
    @Value("${msg.password}")
    private String MSG_PASSWORD;
    @Value("${msg.userip}")
    private String MSG_USER_IP;
    @Value("${msg.appkey}")
    private String MSG_APP_KEY;
    @Value("${msg.channelid}")
    private String MSG_CHANNEL_ID;
    @Value("${msg.url}")
    private String MSG_URL;
    @Value("${msg.cdn_server}")
    private String MSG_CDN_SERVER;

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private AutoTriMapper autoTriMapper;
    @Autowired
    private PushListService pushListService;

    /**
     * 创建运营计划
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject createOperPlan(Map<String, Object> map) {
        ResultObject res = new ResultObject();
        autoTriMapper.createOperPlan(map);
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg(StatusEnum.SUCCESS.getMsg());
        return res;
    }

    /**
     * 更新运营计划
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject updateOperPlan(Map<String, Object> map) {
        ResultObject res = new ResultObject();
        autoTriMapper.updateOperPlan(map);
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg(StatusEnum.SUCCESS.getMsg());
        return res;
    }

    /**
     * 查询运营计划列表
     *
     * @return
     */
    @Override
    public ResultObject queryOperPlan() {
        ResultObject res = new ResultObject();
        res.setData(autoTriMapper.queryOperPlan());
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg(StatusEnum.SUCCESS.getMsg());
        return res;
    }

    /**
     * 创建自动触发策略
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject createAutoTri(Map<String, Object> map) {
        ResultObject res = new ResultObject();
        // 验证是否存在同名策略
        String tacName = String.valueOf(map.get("tacName"));
        Map<String, Object> params = new HashMap<>();
        params.put("tacName", tacName);
        if (autoTriMapper.isTacNameExist(params)) {
            res.setCode(StatusEnum.NAME_ALREADY_EXIST.getCode());
            res.setMsg(StatusEnum.NAME_ALREADY_EXIST.getMsg());
            return res;
        }
        // age转换格式
        if (null != map.get("age[]")) {
            map.put("age", String.valueOf(map.get("age[]")));
        }
        // 生成自动触发策略ID，并将策略详情保存在明细表
        map = bac(map);
        // 创建策略详情
        autoTriMapper.createAutoTriTacInfo(map);
        // 创建策略
        autoTriMapper.createAutoTriTac(map);
        // 创建审批记录
        map.put("approveStatus", 1);
        // 去掉备注
        if (map.get("remark") != null && !"".equals(String.valueOf(map.get("remark")))) {
            map.remove("remark");
        }
        createApproveRecord(map);
        // 创建渠道策略Redis记录
        createChannelTacRecordToRedis(map, 1);
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg("创建成功");
        return res;
    }

    /**
     * 查询策略列表
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject queryAutoTris(Map<String, Object> map) {
        // 超级管理员默认查看所有策略
        if ("1".equals(map.get("isAdmin"))) {
            // 超级管理员有权查询所有审批列表
            map.remove("creatorId");
            map.remove("creatorDepartment");
        }
        // 新增对暂停状态的策略查询
        if (null != map.get("tacStatus") && !"".equals(String.valueOf(map.get("tacStatus")))) {
            if ("5".equals(String.valueOf(map.get("tacStatus")))) {
                map.remove("tacStatus");
                map.put("pause", "0");
            }
        }
        ResultObject res = new ResultObject();
        List<HashMap<String, Object>> datas = new ArrayList<>();
        Integer total = autoTriMapper.queryAutoTriCount(map);
        datas = autoTriMapper.queryAutoTris(map);
        // 分页
        Page<HashMap<String, Object>> page = new Page<>();
        page.setTotal(total);
        page.setData(datas);
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg("查询策略列表成功");
        res.setData(page);
        return res;
    }

    /**
     * 查询指定策略详情
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject queryAutoTriDetail(Map<String, Object> map) {
        // 策略类型： 1-短信策略；2-push策略；3-弹窗策略；4-消息通知；
        String tacMode = String.valueOf(map.get("tacMode"));
        Map<String, Object> data = new HashMap<>();
        // 查询策略详情
        data = autoTriMapper.queryAutoTriTacByTacID(map);
        // 查询策略触发条件
        Map<String, Object> info = new HashMap<>();
        info = autoTriMapper.queryAutoTriTacInfoByTacID(map);
        data = rap(data, info);
        // 分类查询策略内容
        Map<String, Object> detail = new HashMap<>();
        switch (tacMode) {
            case "1":
                detail = autoTriMapper.queryAutoTriTacSMSByTacID(map);
                data = rap(data, detail);
                break;
            case "2":
                detail = autoTriMapper.queryAutoTriTacPUSHByTacID(map);
                data = rap(data, detail);
                break;
            case "3":
                detail = autoTriMapper.queryAutoTriTacPOPByTacID(map);
                data = rap(data, detail);
                break;
            case "4":
                detail = autoTriMapper.queryAutoTriTacMSGByTacID(map);
                data = rap(data, detail);
                break;
        }
        ResultObject res = new ResultObject();
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg(StatusEnum.SUCCESS.getMsg());
        res.setData(data);
        return res;
    }

    /**
     * 修改自动触发策略
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject updateAutoTri(Map<String, Object> map) {
        ResultObject res = new ResultObject();
        // 策略类型： 1-短信策略；2-push策略；3-弹窗策略；4-消息通知；
        String tacMode = String.valueOf(map.get("tacMode"));
        // 验证是否存在同名策略
        String tacName = String.valueOf(map.get("tacName"));
        String tacId = String.valueOf(map.get("tacId"));
        Map<String, Object> params = new HashMap<>();
        params.put("tacName", tacName);
        params.put("tacId", tacId);
        if (autoTriMapper.isTacNameExist(params)) {
            res.setCode(StatusEnum.NAME_ALREADY_EXIST.getCode());
            res.setMsg(StatusEnum.NICK_NAME_EXIST.getMsg());
            return res;
        }
        // age格式转换
        if (null != map.get("age")) {
            map.put("age", String.valueOf(map.get("age")));
        }
        // 将策略状态重置为 1-待审核
        map.put("tacStatus", 1);
        // 更新策略明细
        switch (tacMode) {
            case "1":
                autoTriMapper.updateAutoTriTacSMSByTacID(map);
                break;
            case "2":
                // push策略多模板参数处理
                map.putAll(pushListService.pushDeal(map));
                autoTriMapper.updateAutoTriTacPUSHByTacID(map);
                break;
            case "3":
                autoTriMapper.updateAutoTriTacPOPByTacID(map);
                break;
            case "4":
                autoTriMapper.updateAutoTriTacMSGByTacID(map);
                break;
        }
        // 更新策略触发条件
        autoTriMapper.updateAutoTriTacInfoByTacID(map);
        // 更新策略详情
        autoTriMapper.updateAutoTriTacByTacID(map);
        // 更新渠道策略Redis记录
        createChannelTacRecordToRedis(map, 2);
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg(StatusEnum.SUCCESS.getMsg());
        return res;
    }

    /**
     * 暂停策略
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject pause(Map<String, Object> map) {
        autoTriMapper.pause(map);
        ResultObject res = new ResultObject();
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg("操作成功");
        return res;
    }

    /**
     * 策略审批
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject doApprove(Map<String, Object> map) {
        ResultObject res = new ResultObject();
        // 验证是否是超管或者记录指定审批人
        if (!("1").equals(map.get("isAuditor")) && !("1").equals(map.get("isAdmin"))
            && !map.get("approverId").equals(map.get("creatorId"))) {
            res.setCode(StatusEnum.OPERATE_LIMITED.getCode());
            res.setMsg("当前用户无审批权");
            return res;
        }
        // 审批状态：1-待审批；2-已通过；3-已驳回；
        String approveStatus = String.valueOf(map.get("approveStatus"));
        // 策略明细
        String tacId = String.valueOf(map.get("tacId"));
        Map<String, Object> tac = autoTriMapper.queryAutoTriTacByTacID(map);
        String creatorId = String.valueOf(tac.get("creatorId"));
        String creatorName = String.valueOf(tac.get("creatorName"));
        String creatorDepartment = String.valueOf(tac.get("creatorDepartment"));
        String approverId = String.valueOf(map.get("creatorId"));
        String approverName = String.valueOf(map.get("creatorName"));
        // 新增审批记录
        Map<String, Object> approveRecord = new HashMap<>();
        approveRecord.put("tacId", tacId);
        approveRecord.put("tacName", tac.get("tacName"));
        approveRecord.put("tacMode", tac.get("tacMode"));
        approveRecord.put("creatorId", creatorId);
        approveRecord.put("creatorName", creatorName);
        approveRecord.put("creatorDepartment", creatorDepartment);
        approveRecord.put("approverId", approverId);
        approveRecord.put("approverName", approverName);
        approveRecord.put("approveStatus", approveStatus);
        approveRecord.put("remark", map.get("remark"));
        createApproveRecord(approveRecord);
        // 修改策略状态
        Map<String, Object> modifyTac = new HashMap<>();
        modifyTac.put("tacId", tacId);
        switch (approveStatus) {
            case "2":
                modifyTac.put("tacStatus", 3);
                break;
            case "3":
                modifyTac.put("tacStatus", 2);
                break;
        }
        autoTriMapper.updateAutoTriTacByTacID(modifyTac);
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg(StatusEnum.SUCCESS.getMsg());
        return res;
    }

    /**
     * 查询审批列表
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject queryApproveRecords(Map<String, Object> map) {
        ResultObject res = new ResultObject();
        List<HashMap<String, Object>> datas = new ArrayList<>();
        Integer total;
        if ("1".equals(map.get("isAdmin"))) {
            // 超级管理员有权查询所有审批列表
            map.remove("creatorId");
            map.remove("creatorDepartment");
        } else if ("1".equals(map.get("isAuditor"))) {
            // 审核员可以查看本部门所有审批列表
            map.remove("creatorId");
        }
        // 策略状态：1-待审核；2-已驳回；3-未开始；4-进行中；5-已终止；6-已结束；7-已删除
        List<Byte> tacStatus = new ArrayList<>();
        // 获取指定的审批状态
        // 审批状态：1-待审批；2-已通过；3-已驳回；
        String approveStatus = String.valueOf(map.get("approveStatus"));
        switch (approveStatus) {
            case "1":
                tacStatus.add(Byte.valueOf("1"));
                break;
            case "3":
                tacStatus.add(Byte.valueOf("2"));
                break;
            case "2":
                tacStatus.add(Byte.valueOf("3"));
                tacStatus.add(Byte.valueOf("4"));
                tacStatus.add(Byte.valueOf("5"));
                tacStatus.add(Byte.valueOf("6"));
                break;
        }
        map.put("tacStatus", tacStatus);
        total = autoTriMapper.queryApproveRecordsCount(map);
        datas = autoTriMapper.queryApproveRecords(map);
        // 分页
        Page<HashMap<String, Object>> page = new Page<>();
        page.setTotal(total);
        page.setData(datas);
        res.setMsg("查询审批列表成功");
        res.setData(page);
        return res;
    }

    /**
     * 查询单条策略审批记录
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject queryApproveRecordByTacID(Map<String, Object> map) {
        ResultObject res = new ResultObject();
        List<HashMap<String, Object>> datas = new ArrayList<>();
        String tacId = map.get("tacId").toString();
        datas = autoTriMapper.queryApproveHistoryByTacID(tacId);
        res.setMsg("查询策略审批记录成功");
        res.setData(datas);
        return res;
    }

    /**
     * 实时测试
     *
     * @param map
     * @return
     */
    @Override
    public ResultObject manual(Map<String, Object> map) {
        // 策略类型：1-短信策略；2-push策略；3-弹窗策略；4-消息通知；
        String tacMode = String.valueOf(map.get("tacMode"));
        switch (tacMode) {
            case "1":
                doManualSMS(map);
                break;
            case "2":
                doManualPUSH(map);
                break;
            case "3":
                doManualPOP(map);
                break;
            case "4":
                doManualMSG(map);
                break;
        }
        ResultObject res = new ResultObject();
        res.setCode(StatusEnum.SUCCESS.getCode());
        res.setMsg(StatusEnum.SUCCESS.getMsg());
        return res;
    }

    @Override
    public ResultObject<String> revocation(Map<String, Object> params) {
        // 策略ID不可为空
        if (null == params.get("strategyId")) {
            throw new MyException(StatusEnum.BUSINID, "策略ID不可为空");
        }
        // 执行状态不可为空
        String status = params.get("executeStatus").toString();
        if (status == null) {
            throw new MyException(StatusEnum.BUSINID, "策略执行状态不可为空");
        }
        // 仅【1-待审核】状态下的策略可以撤回
        Byte executeStatus = Byte.parseByte(status);
        if (!executeStatus.equals(StrategyConstants.EXECUTE_CHECKING)) {
            throw new MyException(StatusEnum.BUSINID, "当前策略不可撤回");
        }
        params.put("revocationTime",
            DateUtil.parseDateToStr(new Date(), DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS));
        try {
            autoTriMapper.revocationStrategyById(params);
        } catch (Exception e) {
            log.error("撤回策略失败", e);
            throw new MyException(StatusEnum.ERROR, "撤回策略失败");
        }
        return new ResultObject<String>("0000", "撤回成功", "已成功撤回该策略！");
    }

    /**
     * 生成自动触发策略ID，并将策略详情保存在明细表
     *
     * @param map
     * @return
     */
    private Map<String, Object> bac(Map<String, Object> map) {
        String tacMode = String.valueOf(map.get("tacMode"));
        String tacId = "";
        Date date = new Date();
        switch (Integer.valueOf(tacMode)) {
            case 1:
                log.info("创建短信策略");
                tacId = "ZDDX" + DateUtil.parseDateToStr(date, DateUtil.DATE_TIME_FORMAT_YYYYMMDDHHMISSSSS);
                map.put("tacId", tacId);
                autoTriMapper.createAutoTriTacSMS(map);
                break;

            case 2:
                log.info("创建PUSH策略");
                tacId = "ZDPS" + DateUtil.parseDateToStr(date, DateUtil.DATE_TIME_FORMAT_YYYYMMDDHHMISSSSS);
                map.put("tacId", tacId);
                // push策略多模板参数处理
                map.putAll(pushListService.pushDeal(map));
                if (map.containsKey("triSceneDetail") && map.get("triSceneDetail").toString().equals("关注歌手")) {
                    if (map.containsKey("smsUrlType") && map.get("smsUrlType").toString().equals("1")) {
                        map.remove("pushUrl");
                    }
                }
                map.remove("smsUrlType");
                autoTriMapper.createAutoTriTacPUSH(map);
                break;

            case 3:
                log.info("创建弹窗策略");
                tacId = "ZDTQ" + DateUtil.parseDateToStr(date, DateUtil.DATE_TIME_FORMAT_YYYYMMDDHHMISSSSS);
                map.put("tacId", tacId);
                autoTriMapper.createAutoTriTacPOP(map);
                break;

            case 4:
                log.info("创建消息通知策略");
                tacId = "ZDMN" + DateUtil.parseDateToStr(date, DateUtil.DATE_TIME_FORMAT_YYYYMMDDHHMISSSSS);
                map.put("tacId", tacId);
                autoTriMapper.createAutoTriTacMSG(map);
                break;
        }
        log.info("生成策略ID：{}", tacId);
        return map;
    }

    /**
     * remove and put data
     *
     * @param data
     * @param map
     * @return
     */
    private Map<String, Object> rap(Map<String, Object> data, Map<String, Object> map) {
        map.remove("id");
        map.remove("tacId");
        data.putAll(map);
        return data;
    }

    /**
     * 创建审批记录
     *
     * @param map
     */
    private void createApproveRecord(Map<String, Object> map) {
        autoTriMapper.createApproveRecord(map);
    }

    /**
     * 短信实时下发
     *
     * @param map
     */
    private void doManualSMS(Map<String, Object> map) {
        // 名单ID
        String rosterId = String.valueOf(map.get("rosterId"));
        List<String> msisdns = autoTriMapper.queryMsisdns(rosterId);
        // 短信内容
        String content = String.valueOf(map.get("smsContent"));
        // 短信模板ID
        String templateId = map.get("templateId").toString();
        // 提取参数
        String[] params = getParams(content);
        // 发送短信
        for (String msisdn : msisdns) {
            String[] moblie = {msisdn};
            Sender.sendSMS(templateId, moblie, params);
        }
    }

    /**
     * PUSH实时下发
     *
     * @param map
     */
    private void doManualPUSH(Map<String, Object> map) {
        // push策略多模板参数处理
        map.putAll(pushListService.manualDeal(map));
        String rosterId = String.valueOf(map.get("rosterId"));
        List<String> uuids = autoTriMapper.queryUUIDs(rosterId);
        // 生成taskid
        Date date = new Date();
        String taskid = "PUSH" + DateUtil.parseDateToStr(date, DateUtil.DATE_TIME_FORMAT_YYYYMMDDHHMISSSSS);
        PushContentObject pushContentObject = new PushContentObject();
        // 需要进行HTML解码
        pushContentObject.setPushTitle(HtmlUtils.htmlUnescape(String.valueOf(map.get("pushTitle"))));
        pushContentObject.setPushContent(HtmlUtils.htmlUnescape(map.get("pushContent").toString()));
        // 默认值是1
        pushContentObject.setPushType(1);
        // 1.前端输入链接以：mgmusic开头的，pushLinkType=100
        // 2.输入链接以http或以https开头的，pushLinkType=9
        if (null != map.get("pushUrl")) {
            String pushURL = map.get("pushUrl").toString();
            if (pushURL.startsWith("mgmusic")) {
                pushContentObject.setPushLinkType(100);
            } else {
                pushContentObject.setPushLinkType(9);
            }
            pushContentObject.setPushLinkAddress(pushURL);
        }

        if (null != map.get("picDir")) {
            String picDir = map.get("picDir").toString();
            picDir = IMAGE_SERVER_PATH + picDir.substring(picDir.lastIndexOf("/") + 1);
            pushContentObject.setImgUrl(picDir);
        }
        pushContentObject.setPushCreateTime(System.currentTimeMillis());

        PushRequestBody body = new PushRequestBody();
        // policy固定值
        PushPolicyObject policy = new PushPolicyObject();
        policy.setBroadcast(false);
        policy.setIsOffline(true);
        policy.setPushNetWorkType(0);
        body.setPolicy(policy);
        body.setAppId(PUSH_APP_ID);
        body.setTaskId(taskid);
        body.setContent(pushContentObject);
        body.setUid(uuids);
        body.setTopic(HtmlUtils.htmlUnescape(String.valueOf(map.get("pushTitle"))));
        body.setPushChannel("1");
        body.setIsSync(true);
        // 转Map
        Map<String, Object> params = beanToMap(body);
        // 生成Header
        Map<String, String> header = header(taskid, body);
        // URL
        String url = PUSH_IP + PUSH_URL;
        log.info("服务端URL: {}", url);
        String response = HttpUtils.doPost(url, params, header);
        ApiResponse apiResponse = JsonUtils.json2Obj(response, ApiResponse.class);
    }

    /**
     * 弹窗实时下发
     *
     * @param map
     */
    private void doManualPOP(Map<String, Object> map) {
        // 文件生成日期
        Date date = new Date();
        SimpleDateFormat simpleDateFormat_file = new SimpleDateFormat(DateUtil.DATE_FORMAT_YYYYMMDDHH);
        String fileDate = simpleDateFormat_file.format(date);
        // 名单ID
        String rosterId = String.valueOf(map.get("rosterId"));
        List<String> msisdns = autoTriMapper.queryMsisdns(rosterId);
        String popupId = String.valueOf(map.get("popupId"));
        // 生成文件
        try {
            FileOutputStream fileOutputStream =
                new FileOutputStream(new File(POPUP_LOCAL_PATH + popupId + "_" + fileDate + "_MSISDN"));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream));
            for (String msisdn : msisdns) {
                bufferedWriter.write(msisdn);
                bufferedWriter.newLine();
            }
            bufferedWriter.close();
        } catch (IOException e) {
            log.error("生成文件出错:{}", POPUP_LOCAL_PATH, e);
        }
        // 发送文件
        transport(popupId + "_" + fileDate + "_MSISDN");
    }

    /**
     * 消息通知实时下发
     *
     * @param map
     */
    private void doManualMSG(Map<String, Object> map) {
        // 名单ID
        String rosterId = String.valueOf(map.get("rosterId"));
        List<String> uuids = autoTriMapper.queryUUIDs(rosterId);
        // 生成body
        Body body_str = body(map);
        for (String uuid : uuids) {
            List<String> uid = new ArrayList<>();
            uid.add(uuid);
            MessageInfo messageInfo = new MessageInfo();
            messageInfo.setType(0);
            messageInfo.setAction("11");
            messageInfo.setBody(body_str);
            messageInfo.setTaskId(String.valueOf(System.currentTimeMillis()));
            JSONObject params = new JSONObject();
            params.put("userID", "-1");
            params.put("targetIDList", uid);
            params.put("messageInfo", messageInfo);
            JSONObject sendMessageReq = new JSONObject();
            sendMessageReq.put("sendMessageReq", params);
            doPost(MSG_URL, sendMessageReq.toJSONString());
        }
    }

    /**
     * 提取{}中的参数并以数组形式返回
     *
     * @param str
     * @return
     */
    private String[] getParams(String str) {
        List<String> params = new ArrayList<>();
        // 提取{}中的参数
        Pattern pattern = Pattern.compile("(?<=\\{)[^\\}]+");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            params.add(matcher.group());
        }
        // 提取不到参数
        if (null == params || params.size() == 0) {
            return null;
        }
        // 生成String[]
        String[] tplVars = new String[params.size()];
        for (int i = 0; i < params.size(); i++) {
            tplVars[i] = params.get(i);
        }
        log.info("提取到大括号中的参数:{}", tplVars.toString());
        return tplVars;
    }

    /**
     * JavaBean转换Map
     *
     * @param object
     * @return
     */
    public static Map<String, Object> beanToMap(Object object) {
        Map<String, Object> result = new HashMap<>();
        // 获得类的的属性名数组
        Field[] fields = object.getClass().getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                String name = new String(field.getName());
                result.put(name, field.get(object));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 请求签名 使用场景: 接入方发送HTTP请求时需要对请求做签名校验 签名方式: Map<String, Object> headers = new HashMap<>(); StringBuilder sb = new
     * StringBuilder("MIGU").append("\n"); sb.append(taskId).append("\n"); sb.append(pushChannel).append("\n");
     * sb.append(pushContent).append("\n"); sb.append(timestamp).append("\n"); sb.append(AppSecret).append("\n"); String
     * Authorization = AppKey + ":" + MD5Util.MD5(sb.toString()); headers.put("Authorization", Authorization);
     * headers.put("timestamp", timestamp); 其中: MIGU:固定值 \n:换行转义符 taskId: 任务id,唯一标示 pushChannel: 推送渠道1-个推 当前只支持个推渠道
     * pushContent:传输到推送系统的content.getPushContent() 详见Content Object timestamp:系统时间戳 AppKey :推送系统分配的appKey
     * AppSecret:推送系统分配的密钥 WARN: 接入方系统时间戳跟推送系统需要保持基本一致，防止重放攻击 推送说明: broadcast为true时表示针对app做推送 此时platform生效，表示对手机系统app推送
     * 如果推送列表 broadcast必为false, uid或者cid列表长度大于1。 当前只支持个推推送，所有pushChannel固定为1。 当uid或者cid长度为1时表示对单个用户进行推送。 md5值为小写md5
     */
    private Map<String, String> header(String taskId, PushRequestBody body) {
        Map<String, String> map = new HashMap<>();
        String timestamp = String.valueOf(System.currentTimeMillis());
        StringBuilder sb = new StringBuilder("MIGU").append("\n");
        sb.append(taskId).append("\n");
        sb.append(body.getPushChannel()).append("\n");
        sb.append(body.getContent().getPushContent()).append("\n");
        sb.append(timestamp).append("\n");
        sb.append(PUSH_APP_SECRET).append("\n");
        String Authorization = PUSH_APP_KEY + ":" + DigestUtils.md5Hex(sb.toString());
        map.put("Content-Type", "application/json; charset=utf-8");
        map.put("Authorization", Authorization);
        map.put("timestamp", timestamp);
        return map;
    }

    /**
     * 文件传输
     *
     * @param filename
     */
    private void transport(String filename) {
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.setUseEPSVwithIPv4(true);
            // 连接服务器
            ftpClient.connect(POPUP_IP, POPUP_PORT);
            log.info("IP:{}, PORT:{}", POPUP_IP, POPUP_PORT);
            // 登录ftp
            ftpClient.login(POPUP_USER_NAME, POPUP_PASSWORD);
            log.info("USERNAME:{}, PASSWORD:{}", POPUP_USER_NAME, POPUP_PASSWORD);
            // 设置编码格式
            ftpClient.setControlEncoding("UTF-8");
            // 启动被动模式
            ftpClient.enterLocalPassiveMode();
            // 需要上传的文件
            InputStream inputStream = new FileInputStream(new File(POPUP_LOCAL_PATH + filename));
            // 指定写入的目录,远程路径不存在则创建
            boolean isChanged = ftpClient.changeWorkingDirectory(POPUP_REMOTE_PATH);
            log.info("切换到指定路径:{}，切换结果:{}", POPUP_REMOTE_PATH, isChanged);
            if (isChanged) {
                boolean result = ftpClient.storeFile(new String(filename.getBytes("UTF-8"), "UTF-8"), inputStream);
                log.info("传送文件结果:{}, 文件名:{}", result, filename);
            }
            inputStream.close();
            ftpClient.logout();
        } catch (Exception e) {
            log.error("FTP异常", e);
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    log.info("关闭FTP连接");
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("FTP连接关闭异常", e);
                }
            }
        }
    }

    /**
     * 构造body
     *
     * @param map
     * @return
     */
    private Body body(Map<String, Object> map) {
        String msgNickName = map.get("msgNickName").toString();
        Map<String, Object> info = autoTriMapper.queryMsgTypeInfo(msgNickName);
        Body body = new Body();
        if (null != map.get("msgText")) {
            body.setText(map.get("msgText").toString());
        }
        if (null != map.get("msgPic") && !"".equals(map.get("msgPic").toString())) {
            String msg_pic = map.get("msgPic").toString();
            try {
                body.setPic(URLEncoder.encode(msg_pic, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                log.error("pic's url encode error:{}", e.getMessage());
            }
        }
        if (null != map.get("msgLink")) {
            try {
                body.setLink(URLEncoder.encode(map.get("msgLink").toString(), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                log.error("msg_link's url encode error:{}", e.getMessage());
            }
        }
        if (null != map.get("sourceUrl")) {
            try {
                body.setLink(URLEncoder.encode(map.get("sourceUrl").toString(), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                log.error("msg_link's url encode error:{}", e.getMessage());
            }
        }
        UserSender sender = new UserSender();
        String icon = info.get("icon").toString();
        try {
            sender.setIcon(URLEncoder.encode(icon, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            log.error("icon's url encode error:{}", e.getMessage());
        }
        sender.setNickName(HtmlUtils.htmlEscape(msgNickName));
        body.setSender(sender);
        return body;
    }

    /**
     * header authorization -用户验证信息
     *
     * @return
     */
    private String authorization() {
        // part1： 密码
        String password = MSG_PASSWORD;
        // part2： 6位随机码
        String nonce = getRandom();
        // part3： created时间戳
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtil.DATE_TIME_FORMAT_YYYYMMDDHHMISS);
        String created = simpleDateFormat.format(date);
        // 加密
        String password_base_64 = base_64_encode(sha_256(password + nonce + created));
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("Basicappid=\"");
        stringBuffer.append(MSG_APP_ID);
        stringBuffer.append("\",account=\"");
        stringBuffer.append(MSG_ACCOUNT);
        stringBuffer.append("\",password=\"");
        stringBuffer.append(password_base_64);
        stringBuffer.append("\",nonce=\"");
        stringBuffer.append(nonce);
        stringBuffer.append("\",created=\"");
        stringBuffer.append(created);
        stringBuffer.append("\",userip=\"");
        stringBuffer.append(MSG_USER_IP);
        stringBuffer.append("\",appkey=\"");
        stringBuffer.append(MSG_APP_KEY);
        stringBuffer.append("\",channelid=\"");
        stringBuffer.append(MSG_CHANNEL_ID);
        stringBuffer.append("\"");
        String auth = stringBuffer.toString();
        log.info("header authorization -用户验证信息:{}", auth);
        return auth;
    }

    /**
     * sha-256加密算法
     *
     * @param str
     * @return
     */
    public static byte[] sha_256(String str) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes());
            return messageDigest.digest();
        } catch (NoSuchAlgorithmException e) {
            log.error("SHA-256加密出错:{}", e.getMessage());
        }
        log.error("sha-256加密失败");
        return null;
    }

    /**
     * 获取6位长度随机数
     *
     * @return
     */
    public static String getRandom() {
        StringBuffer val = new StringBuffer();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            val.append(String.valueOf(random.nextInt(10)));
        }
        return val.toString();
    }

    /**
     * base64进行加密
     *
     * @return
     */
    private static String base_64_encode(byte[] bytes) {
        return Base64.encodeBase64String(bytes);
    }

    /**
     * 构造消息通知xml结构
     *
     * @param uids
     * @return
     */
    private String cons_send_message(List<String> uids, JSONObject body_str) {
        JSONObject dataSon = new JSONObject();
        JSONObject dataSonTwo = new JSONObject();
        JSONObject datafat = new JSONObject();
        // 遍历uid
        uids.forEach(e -> dataSonTwo.put("String", e));
        dataSon.put("targetIDList", dataSonTwo);
        dataSon.put("body", body_str);
        dataSon.put("action", "11");
        dataSon.put("type", 0);
        datafat.put("messageInfo", dataSon);
        datafat.put("userId", "-1");
        String s = datafat.toJSONString();
        log.info("调用msg:{}", s);
        return doPost(MSG_URL, s);
    }

    /**
     * HTTP请求
     *
     * @param url
     * @param request
     * @return
     */
    private String doPost(String url, String request) {
        log.info("访问参数:{}", request);

        RestTemplate restTemplate = new RestTemplate();
        // 设置编码集
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", authorization());
        HttpEntity<String> entity = new HttpEntity<>(request, headers);
        ResponseEntity response = restTemplate.postForEntity(url, entity, String.class);
        if (null != response) {
            log.info("消息通知返回参数：{}", response.getBody());
            return response.toString();
        } else {
            return "请求异常！";
        }

    }

    /**
     * 将渠道策略信息保存到redis中[新增，修改，删除]
     *
     * @param map
     * @param status 1-新增; 2-修改;
     */
    private void createChannelTacRecordToRedis(Map<String, Object> map, int status) {
        if (null != map.get("isChannelTac") && "1".equals(String.valueOf(map.get("isChannelTac")))) {
            String redisKey = "new_channel_tacs";
            String tacId = String.valueOf(map.get("tacId"));
            String tacMode = String.valueOf(map.get("tacMode"));
            StringBuffer sb = new StringBuffer();
            if (null != map.get("channelIds")) {
                sb.append(map.get("channelIds"));
            }
            sb.append("_");
            sb.append(tacId);
            sb.append("_");
            sb.append(tacMode);
            sb.append("_");
            switch (String.valueOf(map.get("tacMode"))) {
                case "1":
                    sb.append("1");
                    break;
                case "3":
                    sb.append("4");
                    break;
                default:
                    sb.append("2");
            }
            log.info("key：{}， value：{}", tacId, sb);
            Map<String, String> channel = new HashMap<>();
            channel.put(tacId, String.valueOf(sb));
            if (status == 1) {
                log.info("新增redis记录：{}", redisKey);
                redisTemplate.opsForHash().putAll(redisKey, channel);
            }
            if (status == 2) {
                log.info("删除redis记录：{}", redisKey);
                redisTemplate.opsForHash().delete(redisKey, tacId);
                log.info("新增redis记录：{}", redisKey);
                redisTemplate.opsForHash().putAll(redisKey, channel);
            }
        }
    }
}
